<?php	
  
	abstract class ActiveRecord {
    
    private $_save_message = '';
    private $_failed_validations = array();
    
    static $cached_query_outputs = array();
    static $declared_classkit_methods = array();
    static $count = 0;
    
    static $cache;
    static $class_structures = array();
    static $model_path;
    static $loaded_classes = array();
    
    
    
##### Static methods:
    
    # base, only-one-quering function, end of EACH query
    static function run_query($query) {
        $db =& MDB2::singleton(DSN, unserialize(DB_OPTIONS));
        $db->setErrorHandling(PEAR_ERROR_CALLBACK, array('Error', 'handle_mdb2_error'));
        $db->loadModule('Extended');
        $db->query($query);
        if (!PEAR::isError($db)) {
          return true;
        } else {
          return false;
        }
    }
    
    # base, only-one-quering function, end of EACH query
    static function find_all_with_query ($class_name, $query) {
      $cached_name = md5 ($class_name . $query);
      if (!self::$cache[$cached_name]) {
        ## going back to singleton, using standard DSN for now
        $db =& MDB2::singleton(DSN, unserialize(DB_OPTIONS));
        $db->setErrorHandling(PEAR_ERROR_CALLBACK, array('Error', 'handle_mdb2_error'));
        $db->loadModule('Extended');
        $db->setFetchMode(MDB2_FETCHMODE_OBJECT, $class_name);
        $types = null;
        $params = array();
        $param_types = null;
        $fetchmode = MDB2_FETCHMODE_DEFAULT;
        $rekey = true;
        $force_array = false;
        $group = false;
        $result = $db->getAll($query, $types, $params, $param_types, $fetchmode, $rekey, $force_array, $group);
        Akrudo::raise_info("              returned " . count($result) . "# record(s)]");
        return self::$cache[$cached_name] =& new ActiveRecordCollection ($result, $class_name);
      } else {
        Akrudo::raise_debug("     [cached  {$query}]");
        return self::$cache[$cached_name];
      }
    }
    
    # most important function taking all semi-automated queries
    static function find_with_params ($params) {
      $fields = '*';
      $table = Inflector::tableize($params['class_name']);
      ## going through all of params...
      foreach ($params as $type => $value) {
        switch ($type) {
          case 'order_by':
            $order_by = " ORDER BY " . implode (', ', $value);
          break;
          case 'conditions':
            $conditions = " WHERE " . implode (' AND ', $value);
          break;
          case 'table':
            $table = $value;
          break;
          case 'limit':
            $limit = " LIMIT {$value}";
            $elements = explode (',', $limit);
            $elements = $elements[1] - $elements[0];
          break;
          case 'class_name':
            $class_name = $value;
          break;
        }
      }
      $query = "SELECT {$fields} FROM {$table}{$conditions}{$order_by}{$limit}";
      $result = ActiveRecord::find_all_with_query($class_name, $query);
      if ($elements == 1) {
        return $result->first();
      } else {
        return $result;
      }
    }
    
    # Find all, ar/class_name
    static function find_all ($class_name, $query) {
      if (is_array($query)) {
        return self::find_all_with_params($class_name, $query);
      } else {
        return self::find_all_with_query($class_name, $query);
      }
    }
    
    # base ActiveRecord children function, they have autogenerated functions find()
    static function ar_find($class_name, $items, $params = array()) {
      if (is_array($items)) {
        $params['conditions'][] = "id IN (" . implode (", ", $items) . ")";
      } elseif ($items == all) {
      } elseif ($items == first) {
        $params['limit'] = '0, 1';
      } else {
        $params['conditions'][] = "id = {$items}";
        $params['limit'] = '0, 1';
      }
      $params['class_name'] = $class_name;
      return self::find_with_params($params);
    }
    
    # each ActiveRecord child correspond to this function, with find_all_by
    static function ar_find_all_by($class_name, array $bys, $additional_params = array()) {
      $params = array('class_name' => $class_name);
      foreach ($additional_params as $name => $value) {
        switch ($name) {
          case 'order_by':
          case 'conditions':
            $params[$name][] = $value;
          break;
          case 'class_name':
          case 'limit':
            $params[$name] = $value;
          break;
        }
      }
      foreach ($bys as $name => $value) {
        $params['conditions'][] = $name . self::parse_condition_value($value);
      }
      return self::find_with_params($params);
    }
    
    # each ActiveRecord child correspond to this function, with find_by
    static function ar_find_by($class_name, array $bys, $params = array()) {
      $params['limit'] = '0,1';
      return self::ar_find_all_by($class_name, $bys, $params);
    }
    
    # ar_generate() ~ Static method to generate active record subclass object
    static function ar_generate ($class_name, $construct) {
      $obj = new $class_name ($construct);
      $obj->new_one = true; /////////////////////////////////////////////////////////////////////////////////////////// new one setup, change it to sth
      return $obj;
    }
                
    # ar_create() ~ Static method to create active record subclass object (generates and saves)
    static function ar_create ($class_name, $construct) {
      $obj = new $class_name ($construct);
      $obj->Save();
      return $obj;
    }
    
    # parse value depending on data type
    static function parse_condition_value ($value) {
      if (is_array($value)) {
        return " IN ('" . implode ("', '", $value) . "')";
      } elseif (is_a ($value, 'ActiveCollection') || is_a ($value, 'ActiveRecordCollection')) {
        return " IN ('" . $value->implode ("', '") . "')";
      } else {
        return " = '{$value}'";
      }
    }
    
    # get native message from db resource
    static function get_native_message($pear_error) {
      preg_match('/\[Native message: (.*)\]/', $pear_error->userinfo, $matches);
      return $matches[1];
    }
    
    # laoding class structure from database
    static function load_class_structure($class_name) {
      if (self::$class_structures[$class_name]) {
        Akrudo::raise_debug("     [cached  {$query}]");
        return self::$class_structures[$name];
      }
      
      ## connecting to database
      $connection = DSN;
      $db =& MDB2::singleton($connection, unserialize(DB_OPTIONS));
      $db->loadModule('Extended');
      $driver = substr($connection, 0, strpos($connection, '://'));
      $table_name = Inflector::tableize($class_name);
      switch ($driver) {
        case 'mysql':
          $query = "SHOW FIELDS FROM {$table_name}";
          $fetch_temp = $db->options['fetch_class'];
          $db->setFetchMode(MDB2_FETCHMODE_ASSOC);
          $structure = array();
          $result = $db->getAll($query);
          foreach ($result as $column) {
            $structure[$column['field']] = $column;
          }
          $db->setFetchMode(MDB2_FETCHMODE_OBJECT, $fetch_temp);
        break;
        case 'oci8':
            $query = "SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH, NULLABLE, DATA_DEFAULT FROM all_tab_columns WHERE table_name='" . strtoupper($table_name) . "' OR table_name='" . strtolower($table_name) . "' ORDER BY column_id";
        break;
        default:
          Akrudo::raise_crit("Driver {$driver} not implemented yet for load_structure() method.");
        break;
      }
      self::$class_structures[$class_name] = $structure;
      return $structure;
    }
    
    # Extended
    static function load_class($model_path, $class_name) {
      
      ### Loading already parsed, compiled class
      if (!Akrudo::if_compile_php()) {
        require_once(APP_DIR . 'model' . S . $class_name . '.php');
        
    ##### ActiveRecord real class loading ###
      } else {
        
        # Checking if the file exists
        $basename = $class_name . '.php';
        $filename = $model_path . $basename;
        if (!file_exists($filename))
          Akrudo::raise_crit ("{$filename} doesn't exist!");
          
        # Getting all declared classes
        $declared_classes = array_map('strtolower', get_declared_classes());
        if (in_array(strtolower($class_name), $declared_classes)) {
          Akrudo::raise_info ("Class already loaded. Skipping rest.");
          return;
        }
        # Compiling and loading class from specified directory
        PreCompiler::compile_and_require_file ('app' . S . 'model' . S . $class_name . '.php');     /// This shoudl be changed so $model_path / $filename is used. // changes in PreCompiler
        
        # Checking if loaded class is ActiveRecord extender
        if (!is_subclass_of($class_name, 'ActiveRecord')) {
          Akrudo::raise_debug ("Class is non-ActiveRecord one. Skipping rest.");
        }
        ## checking declared methods
        $declared_methods = get_class_methods($class_name);  //self::$declared_classkit_methods[$class_name] = get_class_methods($class_name);
        
        ## Loading class structure
        $structure = self::load_class_structure($class_name);
          
          
          
        Akrudo::raise_debug ($declared_methods);
          
    ##### Loading standard STATIC methods with classkit
        $methods = array('find', 'delete', 'delete_by', 'generate', 'create', 'get_primary_key', 'find_all_by_sql');
        $methods = array_diff($methods, $declared_methods);
        foreach ($methods as $method_name) {
            $classkit_methods[] = "classkit_method_add ({$class_name}, {$method_name}, '\$params = array()', \"return parent::ar_{$method_name}('{$class_name}', \\\$params);\");";
            $declared_methods[] = $method_name;
        }
        $methods = array('find_by', 'find_all_by');
        $methods = array_diff($methods, $declared_methods);
        foreach ($methods as $method_name) {
            $classkit_methods[] = "classkit_method_add ({$class_name}, {$method_name}, '\$bys = array(), \$params = array()', \"return parent::ar_{$method_name}('{$class_name}', \\\$bys, \\\$params);\");";
            $declared_methods[] = $method_name;
        } 
        $method_name = '__set_state';
        if (!in_array($method_name, $declared_methods)) {
            $classkit_methods[] = "classkit_method_add ({$class_name}, {$method_name}, '\$params = array()', \"return parent::ar_{$method_name}('{$class_name}', \\\$params);\");";
            $declared_methods[] = $method_name;
        }
        
        Akrudo::raise_debug ($declared_methods);
        
        
        
    ##### Checking static variables for declarations
        $class_static_variables = get_class_vars($class_name);
        $declarations = array('has_many' => true, 'has_one' => true, 'belongs_to' => true);  // setting 3 types of declarations
        $class_declarations = array_intersect_key($class_static_variables, $declarations);   // filtering
        Akrudo::raise_debug ($class_declarations);
        
        
        
        $allowed_options = array ('class_name', 'order_by', 'foreign_key', 'conditions', 'dependant', 'through');
        
        
        # Go through declarations
        foreach ($class_declarations as $declaration_type => $declaration) {
        
          # If options are not array, add it to blank array
          if (!is_array($declaration))
            $declaration = array($declaration);
          
          foreach ($declaration as $method_name => $options) {
            if (!is_array($options)) {
              $method_name = $options;
              $options = array();
            }
            
            ## Setting predefined options, checking if any non-allowed have been set
            switch($declaration_type) {
              case 'has_many':
                $predefined_options = array ('class_name' => Inflector::singularize($method_name), 'foreign_key' => Inflector::underscore($class_name) . '_id', 'dependant' => false, 'through' => false);
              break;
              case 'has_one':
                $predefined_options = array ('class_name' => Inflector::singularize($method_name), 'foreign_key' => strtolower(Inflector::singularize($method_name)) . '_id', 'dependant' => false, 'through' => false);
              break;
              case 'belongs_to':
                $predefined_options = array ('class_name' => Inflector::singularize($method_name), 'foreign_key' => strtolower(Inflector::singularize($method_name)) . '_id', 'dependant' => false, 'through' => false);
              break;
            }
            $unallowed_options = array_diff (array_keys($options), $allowed_options);
            if ($unallowed_options) {
              Akrudo::raise_err ("Unallowed options used in {$class_name} class static declaration ('" . implode ("', '", $unallowed_options) . "').");
            }
            
            ## Going through leveled options
            $params = array();
            foreach ($options as $key => $value) {
              switch ($key) {
                case 'conditions':
                  $params['conditions'][] = $value;
                break;
                case 'order_by':
                  $params['order_by'][] = $value;
                break;
              }
            }
            
            ## Merging handed options with predefined
            $params = array_merge ($predefined_options, $options, $params);
            //$method_name = strtolower($method_name);
            $params_exported = var_export($params, true);
            $params_exported = preg_replace ('/[\n\t]/', '', $params_exported);
                
            # Checking if these methods haven't been specified earlier {$class_name} model
            if (!in_array($method_name, $declared_methods)) {
              switch($declaration_type) {
                case 'has_many':
                    $classkit_methods[] = "classkit_method_add('{$class_name}', '{$method_name}', '\$params = array()', \"return \\\$this->ar_get_has_many({$params_exported}, \\\$params);\");";
                    $declared_methods[] = $method_name;
                break;
                case 'has_one':
                    $classkit_methods[] = "classkit_method_add('{$class_name}', '{$method_name}', '\$params = array()', \"return \\\$this->ar_get_has_one({$params_exported}, \\\$params);\");";
                    $declared_methods[] = $method_name;
                break;
                case 'belongs_to':
                    $classkit_methods[] = "classkit_method_add('{$class_name}', '{$method_name}', '\$params = array()', \"return \\\$this->ar_get_belongs_to({$params_exported}, \\\$params);\");";
                    $declared_methods[] = $method_name;
                break;
              }
            }
          }
        }
        
        foreach ($structure as $column) {
          $method_name = $column['field'];
          if (!in_array($method_name, $declared_methods)) {
            $classkit_methods[] = "classkit_method_add ('{$class_name}', '{$method_name}', '', \"return \\\$this->get_attribute('{$method_name}');\");";
            $declared_methods[] = $method_name;
          }
        }
        //pc ($classkit_methods);
        eval (implode ("\n", $classkit_methods));
      }
    }
    
    
    
    
    
##### Public instance methods
    
    
    # Public model declared methods, used for autogenerated functions
    public function ar_get_has_many($params, $additional_params = array()) {
      foreach ($additional_params as $key => $value) {
        switch ($key) {
          case 'conditions':
            $params['conditions'][] = $value;
          break;
          case 'order_by':
            $params['order_by'][] = $value;
          break;
          case 'class_name':
            $params['class_name'] = $value;
          break;
          default:
            Akrudo::raise_err ("Unallowed option used for " . get_class($this) . " ('{$key}').");
          break;
        }
      }
      ## checking if through table has been set
      if ($params['through']) {
        extract($params);
        $tables['main'] = Inflector::tableize($class_name);
        $tables['through'] = $through;
        $params['table'] = implode (', ', $tables);
        $params['conditions'][] = "{$tables['through']}." . strtolower ($class_name) . "_id = {$tables['main']}.id";
        $params['conditions'][] = "{$tables['through']}.{$foreign_key} = {$this->id}";
        $params['fields'] = "{$tables['main']}.*";
      } else {
        $params['conditions'][] = "{$params['foreign_key']} = '{$this->id}'";
      }
      return ActiveRecord::find_with_params($params);
    }
    
    #####
    public function ar_get_has_one($params, $additional_params = array()) {
      $conditions = array();
      $order_by = array();
      $class_name = '';
      foreach ($additional_params as $key => $value) {
        switch ($key) {
          case 'conditions':
            $params['conditions'][] = $value;
          break;
          case 'order_by':
            $params['order_by'][] = $value;
          break;
          case 'class_name':
            $params['class_name'] = $value;
          break;
          default:
            Akrudo::raise_err ("Unallowed option used for " . get_class($this) . " ('{$key}').");
          break;
        }
      }
      $params['conditions'][] = "id = '{$this->$params['foreign_key']}'";
      $params['limit'] = "0,1";
      return ActiveRecord::find_with_params($params);
      
    }
    
    #####
    public function ar_get_belongs_to($params, $additional_params = array()) {
      $conditions = array();
      $order_by = array();
      $class_name = '';
      foreach ($additional_params as $key => $value) {
        switch ($key) {
          case 'conditions':
            $params['conditions'][] = $value;
          break;
          case 'order_by':
            $params['order_by'][] = $value;
          break;
          case 'class_name':
            $params['class_name'] = $value;
          break;
          default:
            Akrudo::raise_err ("Unallowed option used for " . get_class($this) . " ('{$key}').");
          break;
        }
      }
      $params['conditions'][] = "id = '{$this->$params['foreign_key']}'";
      $params['limit'] = "0,1";
      //pc ($params);
      return ActiveRecord::find_with_params($params);
      
    }
    
    
    
    
    
    # __construct()
    function __construct ($construct = false) {
      $this->structure();
      if ($construct && (is_object($construct) || is_array($construct))) {
        $this->set_attributes($construct);
      }
      $this->construct_attributes = clone $this;
      return $this;
    }
    
    # returns structure, from source depending on COMPILE_PHP constant
    public function structure() {
      if (COMPILE_PHP) {
        
      }
      return self::$class_structures[get_class($this)];
    }
    
    # set_attributes() ~ Sets object variables; execution depends on $params type
    public function set_attributes($params) {
      
      if (!is_object($params) && !is_array($params)) {
        Akrudo::raise_notice("'$params' needs to be array or object.");
        return;
      }
      if (is_object($params))
        $params = get_object_vars($params);
      
      if (false){
      /*
      
      foreach ($this->structure() as $column) {
        if (isset($params[$col->field]) && is_array($params[$col->field])) {
          if (!is_a($params[$col->field], 'Date')) {
            switch ($col->type) {
              case 'datetime':
              case 'timestamp':
                $dt = $params[$col->field];
                //echo $params[$col->field] = date("Y-m-d H:i:s", mktime($dt['Time_Hour'], $dt['Time_Minute'], $dt['Time_Second'], $dt['Date_Month'], $dt['Date_Day'], $dt['Date_Year']));
                break;
              case 'date':
                $d = $params[$col->field];
                $params[$col->field] = date("Y-m-d", mktime(0, 0, 0, $d['Date_Month'], $d['Date_Day'], $d['Date_Year']));
                break;
              case 'time':  
                $t = $params[$col->field];
                $params[$col->field] = date("H:i:s", mktime($t['Time_Hour'], $t['Time_Minute'], $t['Time_Second']));
                break;
            }
          }
          switch ($col->type) {
            case 'datetime':
            case 'date':
            case 'time':
            case 'timestamp':
              $params[$col->field] = new Date($params[$col->field]);
              break;
          }
        }                      
      }
      */
      }
      foreach ($params as $attributee => $value) {
        $this->$attributee = $value;
      }
    }

    # destroy() ~ similiar, but instance based
    public function destroy() {
      $this->before_destroy();
      $class_name = get_class($this);
      $table = Inflector::tableize($class_name);
      $query = "DELETE FROM {$table} WHERE id = {$this->id}";
      $status = ActiveRecord::run_query($query);
      if ($status) {
        $this->after_destroy();
        $this->_save_message = "{$class_name} has been successfully deleted!";
        return true;
      } else {
        $this->after_destroy();
        $this->_save_message = "Can't delete this {$class_name}!<br />" . self::get_native_message($db);
        return false;
      }
    }
    public function before_destroy () {}
    public function after_destroy () {}
    
    # updates existing record
    public function update () {
      
      # Class name
      $class_name = get_class($this);
    
      ## runing before actions
      $this->before_update();
      $this->before_save();
      
      # checking validations
      if ($this->failed_validations()) {
        $this->saved = 0;
        $this->_save_message = "Can't save this {$class_name}!<br />Failed validation.";
        Akrudo::raise_info(" Can't save {$class_name}. Validation failed.");
        return false;
      }
      
      ## setting basic parameters
      $table_name = Inflector::tableize($class_name);
      $connection = DSN;
      
      # connecting
      $db =& MDB2::singleton($connection, unserialize(DB_OPTIONS));
      $db->loadModule('Extended');
      
      # loading structure 
      $structure = $this->structure();
      array_shift($structure);
      
      
      # setting object vars
      $object_vars = get_object_vars($this);
      $attributes = array_intersect_key($object_vars, $structure);
      
      //$global_before = defined('GLOBAL_BEFORE_CONDITION') ? "AND " . GLOBAL_BEFORE_CONDITION : '';
      
      $conditions = "id = '{$this->id}'";
      $result = $db->extended->autoExecute($table_name, $attributes, MDB2_AUTOQUERY_UPDATE, $conditions, null);
      //$action->execute($values);
      if (!PEAR::isError($db)) {
          $this->after_save();
          $this->after_update();
          $this->saved = 1;
          $this->_save_message = "{$class_name} has been successfully saved!";
          return true;
      } else {
          $this->after_save();
          $this->after_update();
          $this->saved = 0;
          $this->_save_message = "Can't save {$class_name}!<br />" . self::get_native_message($p);
          return false;
      }
    }
    public function before_update() {}
    public function after_update() {}
    
    # inserts object as new record
    public function insert () {
    
      # Class name
      $class_name = get_class($this);
    
      ## runing before actions
      $this->before_create();
      $this->before_save();
      # checking validations
      if ($this->failed_validations()) {
        $this->saved = 0;
        $this->_save_message = "Can't save {$class_name}!<br />Failed validation.";
        Akrudo::raise_info(" Can't save {$class_name}. Validation failed.");
        return false;
      }
    
      ## setting basic parameters
      $table_name = Inflector::tableize($class_name);
      $connection = DSN;
      
      # connecting
      $db =& MDB2::singleton($connection, unserialize(DB_OPTIONS));
      $db->loadModule('Extended');
      
      # loading structure 
      $structure = $this->structure();
      array_shift($structure);
      
      # setting object vars
      $object_vars = get_object_vars($this);
      $attributes = array_intersect_key($object_vars, $structure);
      
      # skipping insert if no fields set    // might be ok to insert even with no fields;
      if (count($attributes) == 0) {
        Akrudo::raise_debug('No attributtes set, skipping insert');
        return false;
      }
      $result = $db->extended->autoExecute($table_name, $attributes, MDB2_AUTOQUERY_INSERT, null, null);
      if (!PEAR::isError($result)) {
          
          ## creating query
          $values = array();
          $conditions = array();
          foreach ($attributes as $name => $value) {
            if ($value != NULL || $value === 0) {
              $conditions[] = "{$name} = ?";
              $values[] = $value;
            } else {
              $conditions[] = "{$name} IS NULL";
            }
          }
          $conditions = implode (' AND ', $conditions);
          $conditions = "SELECT id FROM {$table_name} WHERE {$conditions} ORDER BY id DESC LIMIT 0,1";
          
          ## Executing the query
          $result = $db->getOne($conditions, null, $values, null);
          $this->id = $result;
          
          $this->_save_message = "{$class_name} has been successfully saved!";
          $this->after_save();
          $this->after_create();
          return true;
      } else {
          $this->_save_message = "Can't save this {$class_name}!<br />" . self::get_native_message($p);
          return false;
      }
    }
    public function before_create() {}
    public function after_create() {}
    
    # saves - inserts/updates object depending if it already existed
    public function save() { 
      # Record update
      if ($this->id) {
        return $this->update();
      # Record insert
      } else {
        return $this->insert();
      }
    }
    public function before_save() {}
    public function after_save() {}
    
    # returns _on_save_message;
    public function on_save_message() {
      return $this->_save_message;
    }
    
    # validates attributes on specified rules, if failed, sets _failed_validations
    public function validate($params = array()) {
      
      foreach ($params as $name => $rule) {
        if (is_array($rule)) {
          $options = $rule;
          $rule = $rule[0];
        } else {
          $options = array('message' => $rule);
        }
        switch ($rule) {
          case password:
            $pass_rule = '/^(?=.*\d)(?=.*([a-z]|[A-Z]))([\x20-\x7E]){6,14}$/';
            preg_match ($pass_rule, $this->$name, $match);
            if (!count($match))
              $this->_failed_validations[$name] = $options['message'];
          case exists:
          case non_empty:
          case non_blank:
            if (trim($this->$name) == '')
              $this->_failed_validations[$name] = $options['message'];
          break;
          case email:
          case valid_email:
            $email_rule = '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}/';
            preg_match ($email_rule, $this->$name, $match);
            if (!count($match))
              $this->_failed_validations[$name] = $options['message'];
          break;
          default: 
            echo $name . ' => ' . $this->$name;
            $rule = "/$rule}/";
            preg_match ($rule, $this->$name, $match);
            if (!count($match))
              $this->_failed_validations[$name] = $options['message'];
          break;
        }
      }
    }
    
    public function failed_validations() {
      if (count($this->_failed_validations)) {
        return $this->_failed_validations;
      } else {
        return false;
      }
    }
    
    # gets attribute as object depending on its type
    public function get_attribute($name) {
      $value = $this->$name;
      $field_structure = $this->_whole_structure[$name];
      preg_match('/^([a-zA-Z]+)\(?([0-9]*)\)?$/', $field_structure->type, $match);
      switch ($match[1]) {
        case 'datetime':
        case 'date':
            return new Date($value);
          break;
        case 'text':
        case 'varchar':
            return new ActiveString($value);
        default:
            return $value;
          break;
      }
    }
    
    # returns object's id
    public function __toString () {
      return $this->id();
    }
    
    # craetes var_export code
    public function ar___set_state($class_name, $args) {
      eval("\$obj = new {$class_name};");
      foreach ($args[0] as $_name => $_value) {
        $obj->$_name = $_value;
      }
      return $obj;
    }
    
    # instead of classkit-generating function it adds it to class code;
    function classkit_function_dump ($class_name, $method, $args, $code) {
      return "  function {$method} ({$args}) {\n      {$code}\n    }\n\n  ";
    }
  
  }
  
?>